import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.util.List;
import java.util.Stack;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.KeyStroke;

/**
 * 
 * Classe responsável por criar os componentes da interface gráfica.
 * 
 * @author Claudio
 *
 */
@SuppressWarnings("serial")
public class Uno extends JFrame {

	private JMenuBar menuBar;
	private JMenu arquivoMenu;
	private JMenuItem novoJogoMenuItem;
	private JMenuItem sairMenuItem;
	private Jogo jogo;
	private Panel jogoPane;


	public Uno() {
		jogo = new Jogo();
		
		initComponents();
	}
	
	private void initComponents() {
		menuBar = new JMenuBar();
        arquivoMenu = new JMenu();
        novoJogoMenuItem = new JMenuItem();
        sairMenuItem   = new JMenuItem();
        
        arquivoMenu.setText("Arquivo");
		arquivoMenu.setMnemonic('A');
        novoJogoMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, InputEvent.ALT_MASK));
        novoJogoMenuItem.setText("Novo Jogo");
        novoJogoMenuItem.setMnemonic('N');
        novoJogoMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                novoJogoMenuItemActionPerformed(evt);
            }
        });
        
        arquivoMenu.add(novoJogoMenuItem);
        
        sairMenuItem.setText("Sair");
        sairMenuItem.setMnemonic('X');
        sairMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                sairMenuItemActionPerformed(evt);
            }
        });
        
        arquivoMenu.add(sairMenuItem);
        menuBar.add(arquivoMenu);
        
        setTitle("UNO - EVP");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
        setName("unoFrameWindow");
        //setIconImage(getIconImage());
        setResizable(true);
        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent evt) {
            	sair();
            }
        });
        
        setJMenuBar(menuBar);
        pack();
        java.awt.Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
        setSize(new java.awt.Dimension(610, 450));
        setLocation((screenSize.width-610)/2,(screenSize.height-450)/2);
        setIconImage(java.awt.Toolkit.getDefaultToolkit().getImage("resources/icon.jpg"));
        
        jogoPane = new Panel(this, jogo);
        getContentPane().add(jogoPane, java.awt.BorderLayout.CENTER);

	}
	
	protected void novoJogoMenuItemActionPerformed(ActionEvent evt) {
		jogo = new Jogo();
		initComponents();
	}

	protected void sairMenuItemActionPerformed(ActionEvent evt) {
		sair();
	}
	
	private void sair() {
		if(JOptionPane.showConfirmDialog(null, "Deseja realmente fechar o jogo?", null, JOptionPane.YES_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.YES_OPTION) {
			System.exit(0);
		}
	}
	
	public static void main(String[] args) {
		new Uno().setVisible(true);
	}

	private class Panel extends JPanel {

		private int cardHSpace = 50;
	    private int cardVSpace = 25;
	    private int cardWidth  = 65;
	    private int cardHeight = 100;
		
		private JButton unoButton;
		private JButton corButton;
		private Jogo jogo;
		private JFrame parentFrame;
		private Rectangle[][] screen = new Rectangle[3][];
		
		public Panel(JFrame parentFrame, Jogo jogo) {
			this.parentFrame = parentFrame;
			this.jogo = jogo;
			
			initComponents();
			
			screen[0] = getCardPositions(jogo.getJogadores().size() -1, 0, 3);
	        screen[1] = getCardPositions(2, 1, 3);
	        screen[2] = getCardPositions(jogo.getJogadorAtual().getMao().size(), 2, 3);
	        
	        this.addMouseListener(new MouseListener());
	        
	        repaint();
		}

		private void initComponents() {

	        unoButton = new javax.swing.JButton();
	        corButton = new javax.swing.JButton();
	        
	        setLayout(null);
	        
	        setBorder(new javax.swing.border.BevelBorder(javax.swing.border.BevelBorder.RAISED));
	        setBackground(new java.awt.Color(0, 153, 51));
	        setPreferredSize(new java.awt.Dimension(610, 450));
	        setMinimumSize(new java.awt.Dimension(610, 450));
	        
	        unoButton.setText("UNO!");
	        unoButton.setBounds(10, 160 , 70, 30);
	        add(unoButton);
	        
	        corButton.setText("");
	        corButton.setEnabled(false);
	        corButton.setBounds(10, 200 , 150, 30);
	        corButton.setBackground(Color.black);
	        add(corButton);

	        unoButton.addActionListener(new java.awt.event.ActionListener() {
	            public void actionPerformed(java.awt.event.ActionEvent evt) {
	                unoButtonActionPerformed(evt);
	            }
	        });
	        
	        setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED, java.awt.Color.gray, java.awt.Color.lightGray, java.awt.Color.black, java.awt.Color.darkGray));
	        setBackground(new java.awt.Color(0, 153, 51));
	        setPreferredSize(new java.awt.Dimension(610, 450));
	        setMinimumSize(new java.awt.Dimension(610, 450));
	    }

		public void paintComponent(Graphics g) {
			super.paintComponent(g);
				
			setBackground(new java.awt.Color(0, 153, 51));
			
			screen[0] = getCardPositions(jogo.getJogadores().size() -1, 0, 3);
	        screen[1] = getCardPositions(2, 1, 3);
	        screen[2] = getCardPositions(jogo.getJogadorAtual().getMao().size(), 2, 3);
			
			corButton.setBackground(getCor(jogo.getCorAtual()));
			corButton.setText(jogo.getCorAtual().name());
			
			drawSentido(g, jogo.getSentidoDoJogo());
			
			drawJogadores(g, jogo.getJogadores());
			
			drawDeck(g, jogo.getDeck());
			
			drawDescarte(g, jogo.getDescarte());
			
			drawJogadorAtual(g, jogo.getJogadorAtual());	
			
		}
		
		private void drawJogadorAtual(Graphics g, Jogador jogadorAtual) {
			List<Carta> mao = jogadorAtual.getMao();
			
			if (mao.size() >= 1)
				drawCarta(g, mao.get(0), 0, 2, jogadorAtual.getNome()+ " ("+mao.size()+")");
			else
				drawCarta(g, null, 0, 2, jogadorAtual.getNome() + " (0)");
			
			if (mao.size() > 1) {
				for (int i = 1; i != mao.size(); i++) {
					drawCarta(g, mao.get(i), i, 2);
				}
			}
		}

		private void drawDescarte(Graphics g, Stack<Carta> descarte) {
			int numCards = descarte.size();
			
			if(numCards != 0) {
				if (numCards < 10)
					drawCarta(g, descarte.peek(), 1, 1);
				else if (numCards < 20)
					drawPile(g, descarte.peek(), 1, 1, 2);
				else if (numCards < 30)
					drawPile(g, descarte.peek(), 1, 1, 3);
				else
					drawPile(g, descarte.peek(), 1, 1, 4);
			}
		}

		private void drawDeck(Graphics g, Stack<Carta> deck) {
			int numCards = deck.size();
			
			if (numCards < 10)
				drawPile(g, null, 0, 1, 1);
			else if (numCards >= 10 && numCards < 20)
				drawPile(g, null, 0, 1, 2);
			else if (numCards >= 20 && numCards < 30)
				drawPile(g, null, 0, 1, 3);
			else
				drawPile(g, null, 0, 1, 4);
		}

		private void drawJogadores(Graphics g, List<Jogador> jogadores) {
			int i = 0;
			for (Jogador jogador : jogadores) {
				if(!jogador.equals(jogo.getJogadorAtual())) {
					drawCarta(g, null, i, 0, jogador.getNome() + " ("+ jogador.getMao().size()+")");
					i++;
				}
			}
		}

		private void drawCarta(Graphics g, Carta card, int cardPos, int cardLevel) {
			Image i = toImage(card);
			
			Point p = screen[cardLevel][cardPos].getLocation();
			
			g.drawImage(i, p.x, p.y, this);
		}

		private void drawCarta(Graphics g, Carta card, int cardPos, int cardLevel, 
		                      String message) {
			Image i = toImage(card);
			
			boolean isValida = false;
			if(card != null) {
				isValida = card.isValida();
			}
			
			Point p = screen[cardLevel][cardPos].getLocation();
			
			g.drawImage(i, p.x, p.y, this);
			
			Graphics2D g2 = (Graphics2D)g;
			
			Font font = new Font("Helvetica", Font.BOLD, 12);
			g2.setColor(isValida? Color.white: Color.darkGray);
			g2.setFont(font);
			
			FontRenderContext context = g2.getFontRenderContext();
			Rectangle2D bounds = font.getStringBounds(message, context);
			
			Point p2 = new Point(p.x + (int)(cardWidth - bounds.getWidth())/2, p.y - 8);
			
			g2.drawString(message, p2.x, p2.y);
			
			if (isValida) {
				
				p2.x -= 30;
				if (p2.x < 0) p2.x = 0;
				
				g2.drawImage(Toolkit.getDefaultToolkit().getImage("resources/uno.jpg"), p2.x, p2.y, this);
			}
		}
		
		private void drawPile(Graphics g, Carta card, int cardPos, int cardLevel, int height) {
			Image i = toImage(card);
			
			Point p = screen[cardLevel][cardPos].getLocation();
			
			int x = p.x;
			int y = p.y;
			
			for (int j = 0; j != height; j += 1) {
				g.drawImage(i, x, y, this);
				x += 3;
				y -= 3;
			}
		}
		
		private Image toImage(Carta carta) {
			String url = "resources/card";
			if(carta!=null) {
				url = url.concat(carta.getCor().toString());
				
				if(carta.getEfeito() != null) {
					url = url.concat(carta.getEfeito().toString());
				} else {
					url = url + "/" + carta.getNumero();
				}
				
				return Toolkit.getDefaultToolkit().getImage(url+".jpg");
			} else {
				return Toolkit.getDefaultToolkit().getImage(url+"/uno.jpg");
			}
		}

		private void drawSentido(Graphics g, Sentido direction) {
			Image i;
			
			if(direction.equals(Sentido.HORARIO)) {
				i = Toolkit.getDefaultToolkit().getImage("resources/horario.png");
			} else {
				i = Toolkit.getDefaultToolkit().getImage("resources/antihorario.png");
			}
			
			int x = (getWidth() - i.getWidth(null))/2 - 7;
			int y = (getHeight()- i.getHeight(null))/2 - 15;
			
			g.drawImage(i, x, y, this);
		}

		private Color getCor(Cor corAtual) {
			Color cor = new Color(0,0,0);
			
			if(corAtual.equals(Cor.VERDE)) {
				cor = new Color(0, 102, 0);
			} else if(corAtual.equals(Cor.AZUL)) {
				cor = new Color(0, 0, 204);
			} else if(corAtual.equals(Cor.AMARELO)) {
				cor = new Color(255, 204, 0);
			} else if(corAtual.equals(Cor.VERMELHO)) {
				cor = new Color(204, 0, 0);
			} else if(corAtual.equals(Cor.ESPECIAL)) return new Color(0  , 0  , 0  );
			
			return cor;
		}

		protected void unoButtonActionPerformed(ActionEvent evt) {
			jogo.getJogadorAtual().setUno(true); 
		}
		
		private Rectangle[] getCardPositions(int cardsWide, int cardLevel, int cardMaxLevels) {
			if (cardsWide == 0) cardsWide = 1;
			
			Rectangle[] rect = new Rectangle[cardsWide];
			
			int xSpace = fitCardSpace(cardWidth, cardHSpace, parentFrame.getWidth(), cardsWide, 10);
			int ySpace = fitCardSpace(cardHeight, cardVSpace, parentFrame.getHeight(), cardMaxLevels, 30);
			
			int x = (parentFrame.getWidth() - (cardsWide-1)*xSpace - cardsWide*cardWidth)/2;
			int y = (parentFrame.getHeight() - cardMaxLevels*ySpace - cardMaxLevels*cardHeight)/2
			        + cardLevel*ySpace + cardLevel*cardHeight - 10; //The extra 10 pixels are to give space to the status message
			
			
			for (int i = 0; i != rect.length; i += 1) {
				rect[i] = new Rectangle(x, y, cardWidth, cardHeight);
				
				x += (xSpace + cardWidth);
			}
			
			return rect;
		}
		
		private int fitCardSpace(int cardWidth, int cardSpace, int maxWidth, int cardsWide, int step) {
			
			boolean outOfScreen = maxWidth - (cardsWide-1)*cardSpace - cardsWide*cardWidth < 10;
			
			while (outOfScreen) {
				cardSpace -= step;
				outOfScreen = maxWidth - (cardsWide-1)*cardSpace - cardsWide*cardWidth < 10;
			}
			
			return cardSpace;
		}
		
		private class MouseListener extends MouseAdapter {
			
			public void mousePressed(MouseEvent evt) {
				
				Point p = evt.getPoint();
				
				for (int i = screen[2].length-1; i != -1; i -= 1) {
					if (screen[2][i].contains(p)) {
						jogo.jogar(i); // Ao clicar em alguma carta da mao do jogador, a carta é passada para o jogo.
					}
				}
				repaint();
			}

		}
		
		

	}
}